package org.rcy.framework.thrift.struct;

import org.apache.thrift.TBase;
import org.apache.thrift.TBaseHelper;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TField;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolUtil;
import org.apache.thrift.protocol.TStruct;
import org.apache.thrift.protocol.TType;
import org.rcy.framework.thrift.metadata.FieldIdEnum;
import org.rcy.framework.thrift.metadata.FieldMetaData;
import org.rcy.framework.thrift.metadata.FieldValueMetaData;
import org.rcy.framework.thrift.serializer.ThriftSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * TBase对象的抽象实现
 * 
 * @author zhouree
 * @param <T>
 */
public abstract class AbstractStruct implements TBase<AbstractStruct, FieldIdEnum> {

	private static final long serialVersionUID = 1L;
	
	protected final Logger logger = LoggerFactory.getLogger(getClass());

	protected static final TStruct ANONYMOUS_STRUCT = new TStruct();

	// 元数据
	private SortedMap<FieldIdEnum, FieldMetaData> metaDataMap = new TreeMap();
	// value
	private SortedMap<FieldIdEnum, Object> valueMap = new TreeMap();
	
	public AbstractStruct() {}
	
	public AbstractStruct(SortedMap<FieldIdEnum, FieldMetaData> metaDataMap) {
		this.metaDataMap = metaDataMap;
	}

	@Override
	public void read(TProtocol iprot) throws TException {
		iprot.readStructBegin();
		while (true) {
			// scheme字段
			TField schemeField = iprot.readFieldBegin();
			if (schemeField.type == TType.STOP) {
				break;
			}
			FieldIdEnum field = this.fieldForId(schemeField.id);
			if (field == null) {
				TProtocolUtil.skip(iprot, schemeField.type);
				continue;
			}
			FieldMetaData metaData = this.metaDataForId(schemeField.id);
			FieldValueMetaData valueMetaData = metaData.valueMetaData;

			if (schemeField.type == valueMetaData.type) {
				Object value = ThriftSerializer.read(iprot, valueMetaData);
				this.setFieldValue(field, value);
			} else {
				logger.warn("thrift read type not equals {}",this.getClass().getSimpleName());
				// 数据类型不匹配，则直接跳过
				TProtocolUtil.skip(iprot, schemeField.type);
			}

			iprot.readFieldEnd();
		}
		iprot.readStructEnd();
	}

	@Override
	public void write(TProtocol oprot) throws TException {
		oprot.writeStructBegin(ANONYMOUS_STRUCT);

		for (FieldIdEnum fieldIdEnum : metaDataMap.keySet()) {
			FieldMetaData fieldMetaData = metaDataMap.get(fieldIdEnum);
			FieldValueMetaData valueMetaData = fieldMetaData.valueMetaData;

			String name = fieldIdEnum.getFieldName();
			byte type = valueMetaData.type;
			short id = fieldIdEnum.getThriftFieldId();
			TField field = new TField(name, type, id);

			Object value = this.getFieldValue(fieldIdEnum);
			if (value != null) {
				oprot.writeFieldBegin(field);
				ThriftSerializer.write(oprot, valueMetaData, value);
				oprot.writeFieldEnd();
			}
		}

		oprot.writeFieldStop();
		oprot.writeStructEnd();
	}

	@Override
	public FieldIdEnum fieldForId(int fieldId) {
		for (FieldIdEnum field : metaDataMap.keySet()) {
			if (field.getThriftFieldId() == fieldId) {
				return field;
			}
		}
		return null;
	}

	public FieldMetaData metaDataForId(int fieldId) {
		for (FieldIdEnum field : metaDataMap.keySet()) {
			if (field.getThriftFieldId() == fieldId) {
				return metaDataMap.get(field);
			}
		}
		return null;
	}

	/**
	 * Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise
	 * //TODO int类型若设置了默认值0则无法判断是否已经设置过值，需要添加额外字段判断
	 */
	@Override
	public boolean isSet(FieldIdEnum field) {
		Object value = getFieldValue(field);
		return value != null;
	}

	@Override
	public Object getFieldValue(FieldIdEnum field) {
		return valueMap.get(field);
	}

	@Override
	public void setFieldValue(FieldIdEnum field, Object value) {
		valueMap.put(field, value);
	}

	@Override
	public void clear() {
		// 清空fieldMap中的TField
		for (FieldIdEnum field : valueMap.keySet()) {
			// TODO 设置基本数据类型的默认值
			this.setFieldValue(field, null);
		}
	}

	@Override
	public int compareTo(AbstractStruct other) {
		if (!getClass().equals(other.getClass())) {
			return getClass().getName().compareTo(other.getClass().getName());
		}

		for (FieldIdEnum field : valueMap.keySet()) {
			int lastComparison = TBaseHelper.compareTo(this.getFieldValue(field), other.getFieldValue(field));
			if (lastComparison != 0) {
				return lastComparison;
			}
		}
		return 0;
	}

	@Override
	public AbstractStruct deepCopy() {
		return null;
	}

	/**
	 * 返回参数列表，用于invoke
	 * 
	 * @return
	 */
	public Object[] getValues() {
		List<Object> vlues = new ArrayList();
		vlues.addAll(valueMap.values());
		return vlues.toArray();
	}

	/**
	 * 添加元数据
	 * @param field
	 * @param metaData
	 */
	protected void addMetaData(FieldIdEnum field, FieldMetaData metaData) {
		metaDataMap.put(field, metaData);
	}
	
	/**
	 * 添加value
	 * 
	 * @param field
	 * @param value
	 */
	protected void addValue(FieldIdEnum field, Object value) {
		valueMap.put(field, value);
	}

	protected SortedMap<FieldIdEnum, Object> getValueMap() {
		return valueMap;
	}
	
	/**
	 * 获取所有父类的属性，父类属性放在数组顶部
	 * @param structClass
	 * @return
	 */
	protected Field[] getAllFields(Class<?> structClass) {
		Field[] fields = new Field[0];
		for (Class<?> superClass = structClass; superClass !=null && superClass != Object.class; superClass = superClass.getSuperclass()) {
			Field[] declaredFields = superClass.getDeclaredFields();
			Field[] joinedFields = new Field[fields.length + declaredFields.length];
	        System.arraycopy(declaredFields, 0, joinedFields, 0, declaredFields.length);
            System.arraycopy(fields, 0, joinedFields, declaredFields.length, fields.length);
			fields = joinedFields;
		}
		return fields;
	}

}
